/*
	Copyright (c) 2012 by karmaleaving@qq.com. All rights RESERVED.
*/
/*

	This file defines a encapsulation of Stream with c functions.
	Not same as std io-stream.

*/


#ifndef __SSTD_STREAM_H__
#define __SSTD_STREAM_H__
#include "config.hpp"
#include "code.hpp"
#include <stdio.h>
#include <stdarg.h>

SSTL_BEGIN
struct StreamMode
{
	enum _ {
		Read        = 0, // read only 
		Modify      = 1, // read and modify
		Create      = 2, // create new and write only
		CreateRead  = 3, // create new, read and write
		Append      = 4, // append
		_N= 5
	};
#define _BIN_MODE "rb\0\0rb+\0wb\0\0wb+\0ab"
#define _BIN_MODE_W 4
#define _TXT_MODE "r\0\0r+\0w\0\0w+\0a"
#define _TXT_MODE_W 3
};
class Stream
{
public:
	typedef FILE * fd_type;
protected:
	fd_type _fd;
protected:
	static const size_t STREAM_BUF_SIZE = 128;
	
	Stream(fd_type file = NULL)
		: _fd(file)
	{
	}
	Stream(Stream const &o)
		: _fd(o._fd)
	{
	}
	void operator = (Stream const &o)
	{
		_fd = o._fd;
	}
public:
	bool operator == (Stream const &o) const
	{
		return _fd == o._fd;
	}
	bool operator != (Stream const &o) const
	{
		return _fd != o._fd;
	}
	fd_type file() const
	{
		return _fd;
	}
	virtual ~Stream()
	{
	}
	bool valid() const
	{
		return _fd != NULL;
	}
	bool eof() const
	{
		return _fd == NULL || ::feof(_fd) != INT_OK;
	}
	operator bool () const
	{
		return !eof();
	}
	bool seek_begin(long off = 0) const
	{
		return ::fseek(_fd, off, SEEK_SET) == INT_OK;
	}
	bool seek_end(long off = 0) const
	{
		return ::fseek(_fd, off, SEEK_END) == INT_OK;
	}
	bool seek(long off) const
	{
		return ::fseek(_fd, off, SEEK_CUR) == INT_OK;
	}
	bool set_pos(fpos_t pos)
	{
		return ::fsetpos(_fd, &pos) == INT_OK;
	}
	bool get_pos(fpos_t &pos)
	{
		return ::fgetpos(_fd, &pos) == INT_OK;
	}
	bool size(fpos_t &size)
	{
		fpos_t pos;
		if(!this->get_pos(pos) || !this->seek_end()
			|| !this->get_pos(size))	return false;
		this->set_pos(pos);
		return true;
	}
	void close() 
	{
		::fclose(_fd);
		_fd = NULL;
	}
	bool open(const char * path, const char * mode)
	{
		if(valid()) this->close();
		_fd = ::fopen(path, mode);
		return _fd != NULL;
	}
	bool save() const
	{
		return ::fflush(_fd) == INT_OK;
	}
};
class StreamBin : public Stream
{
private:
	typedef Stream Base;
	friend class sys;
private:
public:		
	StreamBin(StreamBin const &o)
		: Base(o._fd)
	{
	}
	StreamBin(fd_type file = 0)
		: Stream(file)
	{
	}
	void operator = (StreamBin const &o)
	{
		Base::_fd = o._fd;
	}
	StreamBin(char *path, StreamMode::_ m)
	{
		this->open(path, m);
	}
	override ~StreamBin()
	{
	}
	bool open(char *path, StreamMode::_ m)
	{
		static char mode[] = _BIN_MODE;
		return Stream::open(path, mode + 
			((int)m % StreamMode::_N * _BIN_MODE_W));
	}
	size_t read(void *buf, size_t max_size) const
	{
		if(eof()) return 0;
		return ::fread(buf, sizeof(char), max_size, _fd);
	}
	size_t write(void const *buf, size_t max_size) const
	{
		if(eof()) return 0;
		return ::fwrite(buf, sizeof(char), max_size, _fd);
	}
};
class StreamTxt : public Stream
{
protected:
	typedef Stream Base;
	static const i4_t WBUF_SIZE = 12;
	mutable char wbuf[WBUF_SIZE];
	friend class sys;
public:
	StreamTxt(StreamTxt const & o)
		: Base(o._fd)
	{
	}
	void operator = (StreamTxt const &o)
	{
		Base::_fd = o._fd;
	}
	StreamTxt(fd_type file)
		: Stream(file)
	{
	}
public:
	StreamTxt() : Stream(0)
	{
	}
	override ~StreamTxt()
	{
	}
	StreamTxt(char *path, StreamMode::_ m)
	{
		this->open(path, m);
	}
	bool open(char *path, StreamMode::_ m)
	{		
		static char mode[] = _TXT_MODE;
		return Base::open(path, mode + ((int)m % StreamMode::_N * _TXT_MODE_W));
	}
	// get {
	bool get(i1_t *buf, size_t max_count)
	{
		return ::fgets(buf, max_count, _fd) != NULL;
	}
	bool get(i1_t &val) const
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%c", &val) != EOF);
	}
	bool get(i2_t &val) const
	{
		if(eof()) return false;
		i4_t temp = 0;
		if(::fscanf(_fd, "%d", &temp) != EOF)
		{
			val = temp;
			return true;
		}
		else return false;
	}
	bool get(i4_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%d", &val) != EOF);
	}
	bool get(i8_t &val) const
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%lld", &val) != EOF);
	}
	bool get(u1_t *buf, size_t max_count)
	{
		return ::fgets((char*)buf, max_count, _fd) != NULL;
	}
	bool get(u1_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%c", &val) != EOF);
	}
	bool get(u2_t &val) const
	{
		if(eof()) return false;
		u4_t temp = 0;
		if(::fscanf(_fd, "%u", &temp) != EOF)
		{
			val = temp;
			return true;
		}
		else return false;
	}
	bool get(u4_t &val)const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%u", &val) != EOF);
	}
	bool get(u8_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%llu", &val) != EOF);
	}	
	bool get(float &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%e", &val) != EOF);
	}
	bool get(double &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%le", &val) != EOF);
	}
	template<typename Fmt, typename Type>
	bool get(Type &val) const 
	{
		Fmt temp;
		if(!get(temp)) return false;
		val =temp;	return true;
	}
	// get }
	// getx {
	bool getx(i4_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%x", &val) != EOF);
	}
	bool getx(i8_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%llx", &val) != EOF);
	}
	bool getx(u4_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%x", &val) != EOF);
	}
	bool getx(u8_t &val) const 
	{
		if(eof()) return false;
		return (::fscanf(_fd, "%llx", &val) != EOF);
	}	
	template<typename Fmt, typename Type>
	bool getx(Type &val) const 
	{
		Fmt temp;
		if(!getx(temp)) return false;
		val =temp;	return true;
	}
	// getx }
	// get string {
	i4_t get() const
	{
		if(eof()) return EOF;
		return ::fgetc(_fd);
	}
	template<typename String> const
	bool gets(String &str, bool jump_eol = true) const
	{
		return gets(str, &SSTD::Ascii::IsControl, jump_eol);
	}
	template<typename String, typename Eol> const
	bool gets(String &str, Eol fun, bool jump_eol = true) const
	{
		if(eof()) return false;	
		char temp = 0;
		if(!_getchar(temp)) return false;
		if(jump_eol)
			while(fun(temp)) { if(!_getchar(temp)) return false; }
		else if(fun(temp)) return true;
		str.push_back(temp);
		while(_getchar(temp) && !fun(temp)) str.push_back(temp);
		return true;
	}
	// get string }
	// put {
	bool put(i1_t const *val, int width = 0) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*s", width, val) != EOF);
	}
	bool put(i1_t const &val, int width = 0) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*c", width, val) != EOF);
	}
	bool put(i2_t const &val, int width = 0) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*d", width, (i4_t)val) != EOF);
	}
	bool put(i4_t const &val, int width = 0) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*d", width, val) != EOF);
	}
	bool put(i8_t const &val, int width = 0) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*lld", width, val) != EOF);
	}
	bool put(u1_t const *val, int width = 0) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*s", width, val) != EOF);
	}
	bool put(u1_t const &val, int width = 0) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*c", width, val) != EOF);
	}
	bool put(u2_t const &val, int width = 0) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*u", width, (u4_t)val) != EOF);
	}
	bool put(u4_t const &val, int width = 0)const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*u", width, val) != EOF);
	}
	bool put(u8_t const &val, int width = 0) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*llu", width, val) != EOF);
	}
	bool put(float &val, int width = 0, int precision = 4) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*e", width, val) != EOF);
	}
	bool put(double &val, int width = 0, int precision = 4) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*le", width, val) != EOF);
	}
	bool put(long double &val, int width = 0, int precision = 4) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*lle", width, val) != EOF);
	}	
	template<typename Fmt, typename Type>
	bool put(Type const &val, int width = 0) const 
	{
		Fmt temp = (Fmt)val;
		if(!put(temp)) return false;
		return true;
	}
	// put }
	// putf {
	bool putf(float const &val, int width = 0, int precision = 4) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*f", width, precision, val) != EOF);
	}
	bool putf(double const &val, int width = 0, int precision = 4) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*lf", width, precision, val) != EOF);
	}
	bool putf(long double const &val, int width = 0, int precision = 4) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*llf", width, precision, val) != EOF);
	}
	bool pute(float const &val, int width = 0, int precision = 4) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*e", width, precision, val) != EOF);
	}
	bool pute(double const &val, int width = 0, int precision = 4) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*le", width, precision, val) != EOF);
	}
	bool pute(long double const &val, int width = 0, int precision = 4) const
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*.*lle", width, precision, val) != EOF);
	}
	// putf }
	// putx {
	bool putx(i4_t const &val, int width = 0) const 
	{
		return putx(u4_t(val), width);
	}
	bool putx(i8_t const &val, int width = 0) const 
	{
		return putx(u8_t(val), width);
	}
	bool putx(u4_t const &val, int width = 0) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*x", width, val) != EOF);
	}
	bool putx(u8_t const &val, int width = 0) const 
	{
		if(eof()) return false;
		return (::fprintf(_fd, "%*llx", width, val) != EOF);
	}	
	template<typename Fmt, typename Type>
	bool putx(Type const &val, int width = 0) const 
	{
		Fmt temp;
		if(!putx(temp)) return false;
		val =temp;	return true;
	}
	// putx }
	// put string {
	bool put()
	{
		return ::fprintf(_fd, "\n") != EOF;
	}
	template<typename String>
	bool puts(String const &str)
	{
		return (::fwrite(str.data(), str.size(), 1, _fd) == 1);
	}
	// put string }
	// << >> (different to std::stream) {
	template<typename Type>
	StreamTxt const & operator << (Type const &val) const
	{
		this->put(val);
		return *this;
	}
	template<typename Type>
	StreamTxt const & operator >> (Type &val) const
	{
		this->get(val);
		return *this;
	}
	// << >> (different to std::stream) }
	// read(nth) {
	// return 0 if ok; return the count of failed.
	template<typename Type0>
	int read(Type0 &val0)
	{
		if(!this->get(val0)) return 1;
		return 0;
	}
	template<typename Type0, typename Type1>
	int read(Type0 &val0, Type1 &val1)
	{
		if(!this->read(val0)) return 2;
		return this->read(val1);
	}
	template<typename Type0, typename Type1, typename Type2>
	int read(Type0 &val0, Type1 &val1, Type2 &val2)
	{
		if(!this->read(val0)) return 3;
		return this->read(val1, val2);
	}
	template<typename Type0, typename Type1, typename Type2, typename Type3>
	int read(Type0 &val0, Type1 &val1, Type2 &val2, Type3 &val3)
	{
		if(!this->read(val0)) return 4;
		return this->read(val1, val2, val3);
	}
	template<typename Type0, typename Type1, typename Type2, 
		typename Type3, typename Type4>
	int read(Type0 &val0, Type1 &val1, Type2 &val2, 
		Type3 &val3, Type4 &val4)
	{
		if(!this->read(val0)) return 5;
		return this->read(val1, val2, val3, val4);
	}
	template<typename Type0, typename Type1, typename Type2, 
		typename Type3, typename Type4, typename Type5>
	int read(Type0 &val0, Type1 &val1, Type2 &val2, 
		Type3 &val3, Type4 &val4, Type5 &val5)
	{
		if(!this->read(val0)) return 6;
		return this->read(val1, val2, val3, val4, val5);
	}
	// read(nth) }
	// write(nth) {	
	// return 0 if ok; return the count of failed.
	template<typename Type0>
	int write(size_t width, Type0 const &val0)
	{
		if(!this->put(val0, width)) return 1;
		return 0;
	}
	template<typename Type0, typename Type1>
	int write(size_t width, Type0 const &val0, Type1 const &val1)
	{
		if(!this->write(width, val0)) return 2;
		return this->write(width, val1);
	}
	template<typename Type0, typename Type1, typename Type2>
	int write(size_t width, Type0 const &val0, Type1 const &val1, 
		Type2 const &val2)
	{
		if(!this->write(width, val0)) return 3;
		return this->write(width, val1, val2);
	}
	template<typename Type0, typename Type1, typename Type2, typename Type3>
	int write(size_t width, Type0 const &val0, Type1 const &val1, Type2 const &val2, 
		Type3 const &val3)
	{
		if(!this->write(width, val0)) return 4;
		return this->write(width, val1, val2, val3);
	}
	template<typename Type0, typename Type1, typename Type2, 
		typename Type3, typename Type4>
	int write(size_t width, Type0 const &val0, Type1 const &val1, Type2 const &val2, 
		Type3 const &val3, Type4 const &val4)
	{
		if(!this->write(width, val0)) return 5;
		return this->write(width, val1, val2, val3, val4);
	}
	template<typename Type0, typename Type1, typename Type2, 
		typename Type3, typename Type4, typename Type5>
	int write(size_t width, Type0 const &val0, Type1 const &val1, Type2 const &val2, 
		Type3 const &val3, Type4 const &val4, Type5 const &val5)
	{
		if(!this->write(width, val0)) return 6;
		return this->write(width, val1, val2, val3, val4, val5);
	}
	bool printf(char *fmt, ...)
	{
		va_list args;
		va_start(args, fmt);
		bool res = (::vfprintf(_fd, fmt, args) >= 0);
		va_end(args);
		return res;
	}
	// write(nth) }
protected:
	bool _getchar(char &val) const
	{
		return (::fscanf(_fd, "%c", &val) != EOF);
	}
};
#define USE_STD_IO(in, out) \
	SSTD::StreamTxt in(stdin); \
	SSTD::StreamTxt out(stdout)

SSTL_END
#endif
